package com.gitee.wsl.compose.animation

import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.Easing
import androidx.compose.animation.core.LinearEasing
import com.gitee.wsl.math.Math.mapFloat
import kotlin.math.PI
import kotlin.math.pow
import kotlin.math.sin


val A.Easing.CupertinoEasing: CubicBezierEasing
    get() = CubicBezierEasing( 0.2833f, 0.99f, 0.31833f, 0.99f)

//先加速再减速（这是默认曲线，我们可以不填）
inline val A.Easing.FastOutSlowInEasing get() = androidx.compose.animation.core.FastOutSlowInEasing

//全程减速
inline val A.Easing.LinearOutSlowInEasing get() =  androidx.compose.animation.core.LinearOutSlowInEasing

//全程加速
inline val A.Easing.FastOutLinearInEasing get() = androidx.compose.animation.core.FastOutLinearInEasing

//匀速运动
inline val A.Easing.LinearEasing  get() = androidx.compose.animation.core.LinearEasing


inline val A.Easing.Ease   get() = androidx.compose.animation.core.Ease

inline val A.Easing.EaseOut   get() = androidx.compose.animation.core.EaseOut


inline val A.Easing.EaseIn   get() = androidx.compose.animation.core.EaseIn


inline val A.Easing.EaseInOut   get() = androidx.compose.animation.core.EaseInOut


inline val A.Easing.EaseInSine   get() = androidx.compose.animation.core.EaseInSine


inline val A.Easing.EaseOutSine   get() = androidx.compose.animation.core.EaseOutSine


inline val A.Easing.EaseInOutSine   get() = androidx.compose.animation.core.EaseInOutSine

inline val A.Easing.EaseInCubic   get() = androidx.compose.animation.core.EaseInCubic

inline val A.Easing.EaseOutCubic   get() = androidx.compose.animation.core.EaseOutCubic

inline val A.Easing.EaseInOutCubic   get() = androidx.compose.animation.core.EaseInOutCubic

val A.Easing.EaseInQuint   get() = CubicBezierEasing(0.64f, 0f, 0.78f, 0f)

val A.Easing.EaseOutQuint   get() = CubicBezierEasing(0.22f, 1f, 0.36f, 1f)

val A.Easing.EaseInOutQuint   get() = CubicBezierEasing(0.83f, 0f, 0.17f, 1f)

val A.Easing.EaseInCirc   get() = CubicBezierEasing(0.55f, 0f, 1f, 0.45f)

val A.Easing.EaseOutCirc   get() = CubicBezierEasing(0f, 0.55f, 0.45f, 1f)

val A.Easing.EaseInOutCirc   get() = CubicBezierEasing(0.85f, 0f, 0.15f, 1f)

val A.Easing.EaseInQuad   get() = CubicBezierEasing(0.11f, 0f, 0.5f, 0f)

val A.Easing.EaseOutQuad   get() = CubicBezierEasing(0.5f, 1f, 0.89f, 1f)

val A.Easing.EaseInOutQuad   get() = CubicBezierEasing(0.45f, 0f, 0.55f, 1f)

val A.Easing.EaseInQuart   get() = CubicBezierEasing(0.5f, 0f, 0.75f, 0f)

val A.Easing.EaseOutQuart   get() = CubicBezierEasing(0.25f, 1f, 0.5f, 1f)

val A.Easing.EaseInOutQuart   get() = CubicBezierEasing(0.76f, 0f, 0.24f, 1f)

val A.Easing.EaseInExpo   get() = CubicBezierEasing(0.7f, 0f, 0.84f, 0f)

val A.Easing.EaseOutExpo   get() = CubicBezierEasing(0.16f, 1f, 0.3f, 1f)

val A.Easing.EaseInOutExpo   get() = CubicBezierEasing(0.87f, 0f, 0.13f, 1f)

val A.Easing.EaseInBack   get() = CubicBezierEasing(0.36f, 0f, 0.66f, -0.56f)

val A.Easing.EaseOutBack   get() = CubicBezierEasing(0.34f, 1.56f, 0.64f, 1f)


val A.Easing.EaseInOutBack   get() = CubicBezierEasing(0.68f, -0.6f, 0.32f, 1.6f)


val A.Easing.EaseInElastic   get() = Easing { fraction: Float ->
    val c4 = (2f * PI) / 3f

    return@Easing when (fraction) {
        0f -> 0f
        1f -> 1f
        else ->
            (-(2.0f).pow(10f * fraction - 10.0f) *
                    sin((fraction * 10f - 10.75f) * c4)).toFloat()
    }
}

val A.Easing.EaseOutElastic   get() = Easing { fraction ->
    val c4 = (2f * PI) / 3f

    return@Easing when (fraction) {
        0f -> 0f
        1f -> 1f
        else ->
            ((2.0f).pow(-10.0f * fraction) *
                    sin((fraction * 10f - 0.75f) * c4) + 1f).toFloat()
    }
}

val A.Easing.EaseInOutElastic   get() = Easing { fraction ->
    val c5 = (2f * PI) / 4.5f
    return@Easing when (fraction) {
        0f -> 0f
        1f -> 1f
        in 0f..0.5f ->
            (-(2.0f.pow(20.0f * fraction - 10.0f) *
                    sin((20.0f * fraction - 11.125f) * c5)) / 2.0f).toFloat()
        else ->
            ((2.0f.pow(-20.0f * fraction + 10.0f) *
                    sin((fraction * 20f - 11.125f) * c5)) / 2f).toFloat() + 1f
    }
}

val A.Easing.EaseOutBounce   get() = Easing { fraction ->
    val n1 = 7.5625f
    val d1 = 2.75f
    var newFraction = fraction

    return@Easing if (newFraction < 1f / d1) {
        n1 * newFraction * newFraction
    } else if (newFraction < 2f / d1) {
        newFraction -= 1.5f / d1
        n1 * newFraction * newFraction + 0.75f
    } else if (newFraction < 2.5f / d1) {
        newFraction -= 2.25f / d1
        n1 * newFraction * newFraction + 0.9375f
    } else {
        newFraction -= 2.625f / d1
        n1 * newFraction * newFraction + 0.984375f
    }
}

val A.Easing.EaseInBounce get() = Easing { fraction ->
    return@Easing 1 - EaseOutBounce.transform(1f - fraction)
}

val A.Easing.EaseInOutBounce   get() = Easing { fraction ->
    return@Easing if (fraction < 0.5) {
        (1 - EaseOutBounce.transform(1f - 2f * fraction)) / 2f
    } else {
        (1 + EaseOutBounce.transform((2f * fraction - 1f))) / 2f
    }
}

/**
 * Direct and subtle
 *
 * Usage: Transitions that are functional and utilitarian should use this curve.
 */
val A.Easing.FastInvokeEasing: CubicBezierEasing
    get() = CubicBezierEasing(0f, 0f, 0f, 1f)

/**
 * Bold and emphasizing
 *
 * Usage: Transitions that call attention or reinforce an action should use this curve.
 */
val A.Easing.StrongInvokeEasing: CubicBezierEasing
    get() = CubicBezierEasing(0.13f, 1.62f, 0f, 0.92f)

/**
 * Direct and subtle
 *
 * Usage: Transitions that dismiss a surface without going off screen or within the same area should use this curve combined with a fade out.
 */
val A.Easing.FastDismissEasing: CubicBezierEasing
    get() = FastInvokeEasing

/**
 * Gentle and mellow
 *
 * Usage: Transitions that dismiss a surface off screen while confirming a user action should use this curve.
 */
val A.Easing.SoftDismissEasing: CubicBezierEasing
    get() = CubicBezierEasing(1f, 0f, 1f, 1f)

/**
 * Direct and guiding
 *
 * Usage: Transitions that keep the same element on screen going from one place to another should use this curve.
 */
val PointToPointEasing = CubicBezierEasing(0.55f, 0.55f, 0f, 1f)

/**
 * Quick and efficient
 *
 * Usage: Transitions that keep the same element on screen going from one place to another should use this curve.
 */
val A.Easing.FadeInFadeOutEasing: Easing
    get() = LinearEasing